package main

import (
	"bytes"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/skip2/go-qrcode"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"sync"
	"time"
)

const (
	TOKEN = "weixin" // 替换为你的微信公众平台设置的Token
)

const (
	APPID        = "wx3621d148d6d94d4f"                     // 替换为你的AppID
	APPSECRET    = "9848aa9847446541a991a51381f7b0b7"       // 替换为你的AppSecret
	REDIRECT_URI = "http://u96bc385.natappfree.cc/callback" // 替换为你的回调地址
	//REDIRECT_URI = "http://u96bc385.natappfree.cc/" // 替换为你的回调地址
	//REDIRECT_URI = "u96bc385.natappfree.cc" // 替换为你的回调地址
	SCOPE = "snsapi_userinfo"     // 授权作用域
	STATE = "random_state_string" // 防止CSRF攻击的随机字符串
)

var loginMap sync.Map

func main1() {
	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		sendTemplateMessage(APPID, APPSECRET, "or9LRt7Wggd6PLuhEpuozzIh732E", "SYPB2gdwdwYsY45jN6qlQ1qaFvMzFyKO__seJmp8Jjg")
		c.String(200, "Hello, Gin!")
	})
	// 微信服务器验证URL
	r.GET("/api/oauth/WechatCheck", func(c *gin.Context) {
		signature := c.Query("signature")
		timestamp := c.Query("timestamp")
		nonce := c.Query("nonce")
		echostr := c.Query("echostr")

		if checkSignature(signature, timestamp, nonce) {
			c.String(200, echostr) // 验证成功返回echostr
		} else {
			c.String(403, "Forbidden") // 验证失败
		}
	})

	r.POST("/api/oauth/WechatCheck", handleWeChatMessage)

	r.PUT("/api/oauth/WechatCheck", func(c *gin.Context) {
		signature := c.Query("signature")
		timestamp := c.Query("timestamp")
		nonce := c.Query("nonce")
		//echostr := c.Query("echostr")
		openid := c.Query("openid")

		if checkSignature(signature, timestamp, nonce) {
			c.String(200, openid) // 验证成功返回echostr
		} else {
			c.String(403, "Forbidden") // 验证失败
		}
	})

	r.GET("/wechat/CheckLogin", func(c *gin.Context) {
		loginCode := c.Query("ticket")

		value, ok := loginMap.Load(loginCode)
		if ok {
			c.JSON(http.StatusOK, gin.H{
				"data": value,
			})
		} else {
			c.JSON(http.StatusOK, gin.H{
				"data": "未扫码登录！",
			})
		}

	})

	// 生成二维码页面
	r.GET("/test/qrcode", func(c *gin.Context) {
		// 生成微信授权URL
		ticket := c.Query("ticket")
		authURL := generateAuthURL(ticket)

		// 生成二维码
		qrCode, err := qrcode.New(authURL, qrcode.Medium)
		if err != nil {
			c.String(http.StatusInternalServerError, "Failed to generate QR code")
			return
		}

		// 将二维码返回给前端
		c.Header("Content-Type", "image/png")
		a, _ := qrCode.PNG(256)
		c.Data(http.StatusOK, "image/png", a)
	})

	// 微信回调处理
	r.GET("/api/oauth/WeChatCallback", func(c *gin.Context) {
		code := c.Query("code")
		state := c.Query("state")
		ticket := c.Query("ticket")
		fmt.Println(ticket)

		// 验证state防止CSRF攻击
		if state != STATE {
			c.String(http.StatusBadRequest, "invalid state")
			return
		}

		// 使用code获取access_token
		token, err := getAccessToken(code)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}

		// 获取用户信息
		userInfo, err := getUserInfo(token.AccessToken, token.OpenID)
		if err != nil {
			c.String(http.StatusInternalServerError, err.Error())
			return
		}
		loginMap.Store(ticket, userInfo)
		// 返回用户信息
		c.JSON(http.StatusOK, gin.H{
			"user_info": userInfo,
		})
	})

	r.Run(":9599")
}

// checkSignature 验证微信服务器签名
func checkSignature(signature, timestamp, nonce string) bool {
	// 1. 将token、timestamp、nonce三个参数进行字典序排序
	tmpArr := []string{TOKEN, timestamp, nonce}
	sort.Strings(tmpArr)

	// 2. 将三个参数字符串拼接成一个字符串
	tmpStr := strings.Join(tmpArr, "")

	// 3. 进行sha1加密
	h := sha1.New()
	h.Write([]byte(tmpStr))
	computedSignature := fmt.Sprintf("%x", h.Sum(nil))

	// 4. 与signature对比
	return computedSignature == signature
}

// generateAuthURL 生成微信授权URL
func generateAuthURL(ticket string) string {
	params := url.Values{}
	params.Add("appid", APPID)
	params.Add("redirect_uri", REDIRECT_URI+"?ticket="+ticket)
	params.Add("response_type", "code")
	params.Add("scope", SCOPE)
	params.Add("state", STATE)
	params.Add("connect_redirect", "1")

	return fmt.Sprintf("https://open.weixin.qq.com/connect/oauth2/authorize?%s#wechat_redirect", params.Encode())
}

type AccessToken struct {
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`
	OpenID       string `json:"openid"`
	Scope        string `json:"scope"`
}

// getAccessToken 使用code获取access_token
func getAccessToken(code string) (*AccessToken, error) {
	params := url.Values{}
	params.Add("appid", APPID)
	params.Add("secret", APPSECRET)
	params.Add("code", code)
	params.Add("grant_type", "authorization_code")

	resp, err := http.Get(fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?%s", params.Encode()))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var token AccessToken
	if err := json.Unmarshal(body, &token); err != nil {
		return nil, err
	}

	return &token, nil
}

type UserInfo struct {
	OpenID     string `json:"openid"`
	Nickname   string `json:"nickname"`
	Sex        int    `json:"sex"`
	Province   string `json:"province"`
	City       string `json:"city"`
	Country    string `json:"country"`
	Headimgurl string `json:"headimgurl"`
	UnionID    string `json:"unionid,omitempty"`
}

// getUserInfo 获取用户信息
func getUserInfo(accessToken, openID string) (*UserInfo, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN",
		accessToken, openID)

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var userInfo UserInfo
	if err := json.Unmarshal(body, &userInfo); err != nil {
		return nil, err
	}

	return &userInfo, nil
}

// 处理微信消息
func handleWeChatMessage(c *gin.Context) {
	// 解析XML消息（这里简化处理，实际需解析XML）
	var xmlData struct {
		ToUserName   string `xml:"ToUserName"`
		FromUserName string `xml:"FromUserName"`
		MsgType      string `xml:"MsgType"`
		Content      string `xml:"Content"`
	}

	if err := c.ShouldBindXML(&xmlData); err != nil {
		c.String(http.StatusBadRequest, "Invalid XML")
		return
	}

	// 回复消息（这里简单回复收到的内容）
	response := fmt.Sprintf(`<xml>
		<ToUserName><![CDATA[%s]]></ToUserName>
		<FromUserName><![CDATA[%s]]></FromUserName>
		<CreateTime>%d</CreateTime>
		<MsgType><![CDATA[text]]></MsgType>
		<Content><![CDATA[%s]]></Content>
	</xml>`, xmlData.FromUserName, xmlData.ToUserName, time.Now().Unix(), "已收到消息: "+xmlData.Content)

	c.String(http.StatusOK, response)
}

// 模板消息结构体
type TemplateMessage struct {
	Touser      string          `json:"touser"`        // 接收者OpenID
	TemplateID  string          `json:"template_id"`   // 模板ID
	URL         string          `json:"url,omitempty"` // 点击跳转链接
	Data        map[string]Data `json:"data"`          // 模板数据
	ClientMsgID string          `json:"client_msg_id,omitempty"`
}

type Data struct {
	Value string `json:"value"`
	Color string `json:"color,omitempty"`
}

// 获取AccessToken
func getAccessToken2(appID, appSecret string) (string, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appID, appSecret)
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	var result struct {
		AccessToken string `json:"access_token"`
		ExpiresIn   int    `json:"expires_in"`
		ErrCode     int    `json:"errcode"`
		ErrMsg      string `json:"errmsg"`
	}

	if err := json.Unmarshal(body, &result); err != nil {
		return "", err
	}

	if result.ErrCode != 0 {
		return "", fmt.Errorf("get access token failed: %s", result.ErrMsg)
	}

	return result.AccessToken, nil
}

// 发送模板消息
func sendTemplateMessage(appID, appSecret, openID, templateID string) error {
	accessToken, err := getAccessToken2(appID, appSecret)
	if err != nil {
		return err
	}

	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s", accessToken)

	msg := TemplateMessage{
		Touser:     openID,
		TemplateID: "ZkBWMOhwW1tnnvI4_QYpcWODLdIfgVp4xqT-o_N991M",
		URL:        "https://liuyunshengsir.blog.csdn.net/", // 替换为你的链接
		Data: map[string]Data{
			"openid":   {Value: "测试消息openid！", Color: "#173177"},
			"first":    {Value: "测试消息first", Color: "#173177"},
			"keyword1": {Value: "测试消息keyword1", Color: "#44b549"},
			"keyword2": {Value: "时间keyword2" + time.Now().Format("2006-01-02 15:04:05"), Color: "#173177"},
			"keyword3": {Value: "测试消息keyword3", Color: "#44b549"},
			"keyword4": {Value: "测试消息keyword4", Color: "#44b549"},
			"remark":   {Value: "感谢您的关注！keyword2", Color: "#173177"},
		},
	}

	jsonData, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	fmt.Println(string(body)) // 打印响应结果
	return nil
}
